package controllers

import (
	"bytes"
	"crypto/sha1"
	"encoding/json"
	"fmt"
	"github.com/astaxie/beego"
	"hello/conf"
	"hello/lib"
	"hello/lib/mysqlLib"
	"hello/lib/redisLib"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"sort"
	"strings"
)
const Token = "Dswq1322s1dfsf31s2af321231rew" //替换称自己的token

type WXVerify struct {
	beego.Controller
}
//@router / [get]
func (wx *WXVerify) Get() {
	if wx.GetString("timestamp") != "" && wx.GetString("nonce") != "" && wx.GetString("signature") != "" {
		timestamp, nonce, signatureIn := wx.GetString("timestamp"), wx.GetString("nonce"),wx.GetString("signature")
		signatureGen := makeSignature(timestamp, nonce)
		//将加密后获得的字符串与signature对比，如果一致，说明该请求来源于微信
		if signatureGen != signatureIn {
			log.Printf("signatureGen != signatureIn signatureGen=%s,signatureIn=%s\n", signatureGen, signatureIn)
			wx.Ctx.WriteString("")

		} else {
			//如果请求来自于微信，则原样返回echostr参数内容 以上完成后，接入验证就会生效，开发者配置提交就会成功。
			echostr := wx.GetString("echostr")
			wx.Ctx.WriteString(echostr)
		}
	}

	if wx.GetString("code") != "" {
		code := wx.GetString("code")
		//state := wx.GetString("state")
		log.Printf("code is :%s", code)
		log.Println("已获取code")
		// 通过code换取token
		getOAuthTokenUrl := strings.Replace(conf.OAuthTokenUrl, "APPID", conf.AppId, 1)
		getOAuthTokenUrl = strings.Replace(getOAuthTokenUrl, "SECRET", conf.AppSecret, 1)
		getOAuthTokenUrl = strings.Replace(getOAuthTokenUrl, "CODE", code, 1)
		resp, err := http.Get(getOAuthTokenUrl)
		if err != nil || resp.StatusCode != http.StatusOK {
			log.Printf("get access token from oauth2 error : %s", err)
			return
		}
		defer resp.Body.Close()
		resbody, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			log.Printf("read response error :%s", err)
		}
		if bytes.Contains(resbody, []byte("errcode")) {
			log.Printf("err reponse is :%s", string(resbody))
			errmsg := TokenRespErr{}
			err := json.Unmarshal(resbody, &errmsg)
			if err != nil {
				log.Printf("unmarshal token response msg error : %s", err)
				return
			}
			log.Printf("get access token error, errcode:%d, errmsg:%s", errmsg.Errcode, errmsg.Errmsg)
		} else {
			var res OAuthTokenResp
			err := json.Unmarshal(resbody, &res)
			if err != nil{
				log.Printf("unmarshal token response msg error : %s", err)
			}
			authAccesstoken := res.OAuthAccessToken
			openid := res.Openid
			userInfoUrl := strings.Replace(conf.UserInfoUrl, "ACCESS_TOKEN", authAccesstoken, 1)
			userInfoUrl = strings.Replace(userInfoUrl, "OPENID", openid, 1)

			resp, err = http.Get(userInfoUrl)
			var userinfo UserInfo
			var userId int64
			if resp != nil {
				respbyte, err := ioutil.ReadAll(resp.Body)
				if err != nil {
					log.Printf("get response body error :%s", err)
				}
				err = json.Unmarshal(respbyte, &userinfo)
				if err != nil {
					log.Printf("json unmarshal error :%s", err)
				}
				queryResults , err := mysqlLib.Query("select user_id from hh_user where openid = ?;", userinfo.Openid)
				if err != nil {
					log.Printf("mysql query error :%s", err)
				}
				if len(queryResults) == 0 {
					// generate user_id
					userId, err = lib.CreateUserID()
					// get & insert  userinfo into mysql database
					err = mysqlLib.Exec("insert into hh_user(user_id, openid, sex, province, city, country, headimgurl, privilege, unionid, status) values (?,?,?,?,?,?,?,?,?,?)",
						userId, userinfo.Openid, userinfo.Sex, userinfo.Province, userinfo.City, userinfo.Country, userinfo.Headimgurl, 0, userinfo.Unionid, 1)
					if err != nil {
						log.Printf("insert user info error : %s ", err)
					}
				} else {
					userid := queryResults[0].(int64)
					log.Printf("userid === :%d", userid)
					userId = userid
				}
			}

			//uuid 对应 openid，设置cookie为sessionToken
			sessionToken := lib.GenerateUUID()
			wx.Ctx.SetCookie(conf.UseridIdCookieId, sessionToken, redisLib.SessionExpireTime)

			// 将sessionToken与userId存入redis
			err = redisLib.Set(redisLib.Session, sessionToken, userId, redisLib.SessionExpireTime)
			if err != nil {
				log.Printf( "set token openid error : %s", err)
				return
			}
		}
		wx.Redirect("/home", 302)
	}

}

func makeSignature(timestamp, nonce string) string {

	//1. 将 plat_token、timestamp、nonce三个参数进行字典序排序
	sl := []string{Token, timestamp, nonce}
	sort.Strings(sl)
	//2. 将三个参数字符串拼接成一个字符串进行sha1加密
	s := sha1.New()
	_, err := io.WriteString(s, strings.Join(sl, ""))
	if err != nil {
		log.Printf("error is %v", err)
		return err.Error()
	}
	return fmt.Sprintf("%x", s.Sum(nil))
}
